#include "SocketConcurrentPoolDemo.h"
#include "ThreadPool.h"
#include "SocketCompat.h"
#include <iostream>

using namespace std;
using namespace common;

void socketConcurrentPoolServer()
{
    cout << "案例三：基于线程池处理并发服务器（流式tcp）" << endl;

#ifdef _WIN32
    // windows 下需要装载网络访问库
    WSADATA lpWSAData;
    WSAStartup(MAKEWORD(2, 2), &lpWSAData); // MAKEWORD 拼接版本号，高低位表示版本， 这里事2.2版本
#endif

    // 1、创建socket
    // AF_INET: ipv4 格式的 ip 地址
    // AF_INET6: ipv6 格式的 ip 地址
    // SOCKET：就相当于一个通道或者事一个文件操作符，本质上网络通信也是IO输入输出的一种，所以网络通信实际上和文件或字符操作差不多，都会开启一片内存对其操作，读或写。
    //         实际上网络操作有两片内存区，一块负责接受（读取其他网络写入的信息）、一块负责传输（写入要传递给其他网络的信息）。所以socket创建成功后就相当于获得了两块内存在不使用的时候需要关闭。
    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == -1)
    {
        cerr << "创建失败！！" << endl;
        return;
    }

    // 2、bind
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    // INADDR_NONE：        表示无效的IP地址，用于表示错误或无效的地址。
    // INADDR_BROADCAST：   表示广播地址，用于向网络中的所有主机发送数据。
    // INADDR_LOOPBACK：    表示回环地址，用于将数据发送到本地主机上的回环接口。
    // INADDR_ANY：         表示通配地址，用于指示可以接受来自任何网络接口的数据。
    serverAddr.sin_addr.s_addr = INADDR_ANY; // 使用 INADDR_ANY 等效于 inet_addr("127.0.0.1"); 在绑定时会自动寻找网卡的IP地址予以绑定，INADDR_ANY 值为 0 即，0.0.0.0 IP
    serverAddr.sin_port = htons(9999);       // 端口使用网络字节序，即大端字节序，函数名称记忆理解：h -> host（主机）; n -> net(网络); s-> short(短整型)。 主机to网络的短整型(htons)
    int bindRet = ::bind(serverSocket, (sockaddr *)&serverAddr, sizeof(serverAddr));
    if (bindRet == -1)
    {
        cerr << "绑定端口失败！！！" << endl;
        compatCloseSocket(serverSocket);
        return;
    }

    // 3、监听
    // 要监听的 socket，一次可以获取多少个终端链接，这里只是一次并发可接受，不代表总量，即一次可接受100，但可接受无数次。
    int listenRet = listen(serverSocket, 100);
    if (listenRet == -1)
    {
        cerr << "监听失败！！！" << endl;
        compatCloseSocket(serverSocket);
        return;
    }

    // 4、接收链接客户端
    ThreadPool threadPool(3, 10);

    struct SocketInfo
    {
        sockaddr_in addr;
        SOCKET socket;
    } clientSocketInfoArray[100];

    // 5、读取处理
    TaskCallBack readClientSocket = [](void *const arg) -> void
    {
        SocketInfo *clientSocketInfo = (SocketInfo *)arg;
        // 开始读取客户端发送数据
        char readBuff[1024];
        while (true)
        {
            // memset(readBuff, 0, sizeof(readBuff));
            // int readRet = read(clientSocketInfo->socket, readBuff, sizeof(readBuff));  // linux 下存在的方法。
            int readRet = recv(clientSocketInfo->socket, readBuff, 1024, 0); // linux 和 windows 都支持
            if (readRet > 0)
            {
                // 成功读取数据, 返回值表示为接受数据的长度
                cout << "收到客户端（" << inet_ntoa(clientSocketInfo->addr.sin_addr) << ":" << ntohs(clientSocketInfo->addr.sin_port) << "）数据: " << readBuff << endl;
                // write(clientSocketInfo->socket, readBuff, readRet);                  // linux 下存在的方法。
                send(clientSocketInfo->socket, readBuff, readRet, 0); // linux 和 windows 都支持
            }
            else if (readRet == 0)
            {
                // 客户端链接已断开
                cout << "客户端断开链接！" << endl;
                break;
            }
            else
            {
                // 读取失败，异常
                cout << "读取失败异常: " << endl;
                break;
            }
        }
        // 6. 关闭链接
        compatCloseSocket(clientSocketInfo->socket);
        clientSocketInfo->socket = -1;
    };

    // * 初始化客户端Socket数组
    const int length = sizeof(clientSocketInfoArray) / sizeof(clientSocketInfoArray[0]);
    for (int index = 0; index < length; ++index)
    {
        memset(&clientSocketInfoArray[index], 0, sizeof(clientSocketInfoArray[index]));
        clientSocketInfoArray[index].socket = -1;
    }

    // * 定义接受客户端的结构体
    addrlen clientAddrLength = sizeof(sockaddr_in);
    SocketInfo *clientSocketInfo;
    while (true)
    {
        // * 选中闲置容器用于存储连接的客户端
        for (int index = 0; index < length; ++index)
        {
            if (clientSocketInfoArray[index].socket == -1)
            {
                clientSocketInfo = &clientSocketInfoArray[index];
                break;
            }
        }

        // * 保存连接客户端信息
        clientSocketInfo->socket = accept(serverSocket, (sockaddr *)&clientSocketInfo->addr, &clientAddrLength);
        if (clientSocketInfo->socket == -1)
        {
            cerr << "接受客户端异常！！！" << endl;
            continue;
        }

        // 打印一下连接过来的客户端信息
        cout << "客户端IP：" << inet_ntoa(clientSocketInfo->addr.sin_addr) << "；端口：" << ntohs(clientSocketInfo->addr.sin_port) << "已连接" << endl;
        
        // 线程池添加一个处理客户端读取操作的任务负责读取客户端数据
        threadPool.addTask(readClientSocket, clientSocketInfo, nullptr);
    }

    // 6. 关闭链接
    compatCloseSocket(serverSocket);

#ifdef _WIN32
    // windows 平台下结束了还需要移除网络库的装载
    WSACleanup();
#endif
}
